package com.young.common.config;

import com.young.common.util.CollectionUtil;
import com.young.common.util.JDBCUtil;
import com.young.common.util.JsonUtil;
import com.young.common.util.StringUtils;
import com.young.interfaces.config.model.ConfigDTO;
import com.young.interfaces.config.service.IConfigOutService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.*;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.logging.DeferredLog;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;

/**
 * 自定义配置加载
 * Created by Admin on 2021/6/20.
 */
@Order
public class YoungEnvironmentPostProcessor implements EnvironmentPostProcessor, ApplicationListener<ApplicationReadyEvent> {

//    private static final Logger logger = LoggerFactory.getLogger(YoungEnvironmentPostProcessor.class);

    /**
     * 这个时候Log系统还没有初始化  使用DeferredLog来记录  并在onApplicationEvent进行回放
     */
    private static final DeferredLog logger = new DeferredLog();

    /**
     * 第一次初始化是否完成
     */
    private volatile boolean firstOver = false;

    /**
     * 配置服务
     */
    @Autowired(required = false)
    private IConfigOutService configOutService;


    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationEvent) {
        if (firstOver) {
            System.out.println(String.format("[自定义配置加载] onApplicationEvent 已完成第一次加载,开始重放日志,时间戳=%s,类加载器=%s"
                    , System.currentTimeMillis(), this.getClass().getClassLoader()));
            logger.replayTo(YoungEnvironmentPostProcessor.class);
        } else {
            System.out.println(String.format("[自定义配置加载] onApplicationEvent 是否完成第一次加载=%s,本次bean=%s,时间戳=%s,类加载器=%s"
                    , firstOver, this, System.currentTimeMillis(), this.getClass().getClassLoader()));
        }
    }


    @Override
    public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
        logger.info("[自定义配置加载] 开始加载配置.");
        if (CollectionUtil.isEmpty(configurableEnvironment.getActiveProfiles())) {
            logger.info("[自定义配置加载] 活跃配置为空,pass.");
            return;
        }

        //查询配置
        List<ConfigDTO> configList;
        if (configOutService == null) {
            configList = this.queryConfigListFromDB(configurableEnvironment);
        } else {
            configList = this.queryConfigListFromService();
        }

        logger.info("[自定义配置加载] 查询到配置信息=" + JsonUtil.toJson(configList));

        if (CollectionUtil.isNotEmpty(configList)) {
            // 高优先级配置
            Properties highPriority = this.parsingProperties4HighPriority(configList);
            if (CollectionUtil.isNotEmpty(highPriority)) {
                //创建参数资源并插入上下文
                PropertiesPropertySource source = new PropertiesPropertySource("YoungCustomHigh", highPriority);
                // addFirst表示最高优先级, 配置参数时需要保证参数的正确性
                configurableEnvironment.getPropertySources().addFirst(source);
                logger.info("[自定义配置加载] 加载高优先级配置=" + highPriority);
            }

            // 低优先级配置
            Properties lowPriority = this.parsingProperties4LowPriority(configList);
            if (CollectionUtil.isNotEmpty(lowPriority)) {
                //创建参数资源并插入上下文
                PropertiesPropertySource source = new PropertiesPropertySource("YoungCustomLow", lowPriority);
                // 低优先级
                configurableEnvironment.getPropertySources().addLast(source);
                logger.info("[自定义配置加载] 加载低优先级配置=" + lowPriority);
            }
        }

        logger.info("[自定义配置加载] 加载完毕.");
        firstOver = true;
        System.out.println(String.format("[自定义配置加载] 加载完毕,firstOver=%s,bean=%s,时间戳=%s,类加载器=%s", firstOver, this, System.currentTimeMillis(), this.getClass().getClassLoader()));

        logger.replayTo(YoungEnvironmentPostProcessor.class);
    }

    /**
     * 配置转Properties
     *
     * @param list
     * @return
     */
    private Properties parsingProperties4HighPriority(List<ConfigDTO> list) {
        Properties properties = new Properties();
        if (CollectionUtil.isNotEmpty(list)) {
            for (ConfigDTO entry : list) {
                if (StringUtils.isBlank(entry.getKey())) {
                    continue;
                }
                if (entry.highPriority()) {
                    properties.put(entry.getKey(), entry.getValue());
                }
            }
        }
        return properties;
    }

    /**
     * 配置转Properties
     *
     * @param list
     * @return
     */
    private Properties parsingProperties4LowPriority(List<ConfigDTO> list) {
        Properties properties = new Properties();
        if (CollectionUtil.isNotEmpty(list)) {
            for (ConfigDTO entry : list) {
                if (StringUtils.isBlank(entry.getKey())) {
                    continue;
                }
                if (!entry.highPriority()) {
                    properties.put(entry.getKey(), entry.getValue());
                }
            }
        }
        return properties;
    }

    /**
     * 查询配置
     *
     * @return
     */
    private List<ConfigDTO> queryConfigListFromService() {
        return configOutService.getApplicationConfigList();
    }

    /**
     * 查询配置
     *
     * @param configurableEnvironment
     * @return
     */
    private List<ConfigDTO> queryConfigListFromDB(ConfigurableEnvironment configurableEnvironment) {
        //解析配置中心db配置
        UCCDBProperty uccdbProperty = this.parsingUCCDBProperty(configurableEnvironment);

        if (!uccdbProperty.completed()) {
            logger.error("[自定义配置加载] 获取jdbc配置失败");
            System.out.println("[自定义配置加载] 获取jdbc配置失败");
            throw new RuntimeException("自定义配置加载:获取jdbc配置失败");
        }

        //获取jdbc链接
        Connection conn = JDBCUtil.getConnection(uccdbProperty.getDriver(), uccdbProperty.getUrl(), uccdbProperty.getUser(), uccdbProperty.getPwd());
        //查询配置sql
        String searchConfigSql = "select config_key, config_value, config_group, config_type, priority, state from tb_config_kv where state = 1";
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        List<ConfigDTO> configList = new ArrayList<>();
        try {
            statement = conn.prepareStatement(searchConfigSql);
            resultSet = statement.executeQuery();

            while (resultSet.next()) {
                ConfigDTO configDTO = ConfigDTO.build(resultSet.getString("config_key"),
                        resultSet.getString("config_value"),
                        resultSet.getInt("priority"));
                configList.add(configDTO);
            }

        } catch (Throwable e) {
            logger.error("[自定义配置加载] 获取PreparedStatement异常", e);
            System.out.println("[自定义配置加载] 获取PreparedStatement异常" + e.getMessage());
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            JDBCUtil.closeConnection(conn, statement, resultSet);
        }
        return configList;
    }

    /**
     * 解析配置中心db配置
     *
     * @param configurableEnvironment
     * @return
     */
    private UCCDBProperty parsingUCCDBProperty(ConfigurableEnvironment configurableEnvironment) {
        UCCDBProperty uccdbProperty = new UCCDBProperty();
        for (String name : configurableEnvironment.getActiveProfiles()) {
            PropertySource propertySource = configurableEnvironment.getPropertySources().get(name);
            if (propertySource != null) {
                uccdbProperty.init(propertySource);
            } else {
                try {
                    Properties properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource("application-" + name + ".yml"));
                    uccdbProperty.init(properties);
                } catch (IOException e) {
                    logger.error("[自定义配置加载] 加载active配置文件失败", e);
                    System.out.println("[自定义配置加载] 加载active配置文件失败" + e.getMessage());
                    e.printStackTrace();
                }
            }
            if (uccdbProperty.completed()) {
                continue;
            }
        }
        return uccdbProperty;
    }


}
